// Copyright 2005 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry5.portlet;

import java.io.IOException;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.Portlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
import javax.portlet.ResourceServingPortlet;
import javax.servlet.FilterConfig;

import org.apache.tapestry5.ioc.Registry;
import org.apache.tapestry5.portlet.services.PortletActionRequestHandler;
import org.apache.tapestry5.portlet.services.PortletConfigProvider;
import org.apache.tapestry5.portlet.services.PortletRenderRequestHandler;
import org.apache.tapestry5.portlet.services.PortletResourceRequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Portlet implementation for Tapestry Portlet applilcations. It's job is to create and manage the
 * HiveMind registry, to use the <code>tapestry.portlet.PortletApplicationInitializer</code>
 * service to initialize HiveMind, and the delegate requests to the
 * <code>tapestry.portlet.ActionRequestServicer</code> and
 * <code>tapestry.portlet.RenderRequestServicer</code> services.
 * 
 * @author Howard M. Lewis Ship
 * @since 4.0
 */
public class ApplicationPortlet implements Portlet, ResourceServingPortlet {

    private Logger                logger = LoggerFactory.getLogger(ApplicationPortlet.class);

    private Registry              _registry;

    PortletActionRequestHandler   _actionHandler;

    PortletRenderRequestHandler   _renderHandler;

    PortletResourceRequestHandler _resourceHandler;

    PortletConfigProvider         _portletConfigProvider;

    private PortletConfig         portletConfig;

    @Override
    public void init(PortletConfig portletConfig) throws PortletException {
        this.logger.info("ApplicationPortlet.init()");

        this.portletConfig = portletConfig;

        PortletContext context = portletConfig.getPortletContext();

        this._registry = PortletUtilities.getRegistry(portletConfig);

        this._actionHandler = this._registry.getService("PortletActionRequestHandler",
            PortletActionRequestHandler.class);
        this._renderHandler = this._registry.getService("PortletRenderRequestHandler",
            PortletRenderRequestHandler.class);
        this._resourceHandler = this._registry.getService("PortletResourceRequestHandler",
            PortletResourceRequestHandler.class);

        this._portletConfigProvider = this._registry.getService(PortletConfigProvider.class);

        this.init(this._registry);
    }

    /**
     * Invoked from {@link #init(FilterConfig)} after the Registry has been created, to allow any
     * additional initialization to occur. This implementation does nothing, and my be overriden in
     * subclasses.
     * 
     * @param registry
     *            from which services may be extracted
     * @throws PortletException
     */
    protected void init(Registry registry) throws PortletException {

    }

    @Override
    public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {

        this._portletConfigProvider.store(this.portletConfig);

        try {
            this._actionHandler.service(request, response);
        }
        finally {
            this._registry.cleanupThread();
        }
    }

    @Override
    public void render(RenderRequest request, RenderResponse response) throws PortletException, IOException {

        this._portletConfigProvider.store(this.portletConfig);

        try {
            this._renderHandler.service(request, response);
        }
        finally {
            this._registry.cleanupThread();
        }
    }

    @Override
    public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException {

        this._portletConfigProvider.store(this.portletConfig);

        try {
            this._resourceHandler.service(request, response);
        }
        finally {
            this._registry.cleanupThread();
        }
    }

    /** Shuts down and discards the registry. */
    @Override
    public final void destroy() {
        this.destroy(this._registry);

        // TODO: the instance of Registry is shared accross multiple portlets.
        //       so only the last portlet instance should call shutdown().
        //       ideally the Registry would ref count the number of instances and would automatically shutdown itself
        //       after the last referance has been returned.

        //        this._registry.shutdown();

        this._registry = null;
        this._actionHandler = null;
        this._renderHandler = null;
    }

    /**
     * Invoked from {@link #destroy()} to allow subclasses to add additional shutdown logic to the
     * filter. The Registry will be shutdown after this call. This implementation does nothing, and
     * may be overridden in subclasses.
     * 
     * @param registry
     */
    protected void destroy(Registry registry) {

    }

}
